#pragma once

#include <cv.h>
#include <cvaux.h>
#include <fstream>
#include "highgui.h"

using namespace std;


#define MAXFeat 1000
#define Blockwidth 5
#define Blockheight 5
#define HistBin 36
#define EPSILON 1.0e-6
#define PI 3.14159265358979323846
#define TEST

typedef struct _tagFeatureOP
{
	CvPoint2D32f Feature;
	CvPoint2D32f Vector;
	double angle;
	double hypotenuse;
}FeatureOP, *pFeatureOP;

class OpticalFlow
{
private:
	IplImage *output;
	IplImage *NextImg;
	IplImage *PreImg;
	IplImage *CurImg;
	IplImage *EigImg;
	IplImage *TmpImg;
	IplImage *PyramidCur;
	IplImage *PyramidPrev;
	IplImage *PyramidCur2;
	IplImage *PyramidPrev2;
	IplImage *pImgBgModel;

	int m_iMItypePrev;
	int m_iMItypeCur;

#ifdef TEST
	IplImage *FrModel;
	IplImage *BkModel;
#endif
	CvMat* pFrameMat;
	CvMat* pFrMat;
	CvMat* pBkMat;

	float m_fEnergyfw;
	float m_fEnergybw;
	//float fEnergycur;
	float m_fEnergyDiff;

	float *FWHist;
	float *BWHist;
	float **FVJointHist;

// 	float FWHist[HistBin];
// 	float BWHist[HistBin];
// 	float FVJointHist[HistBin][HistBin];

	float m_fEntropyFw;
	float m_fEntropyBw;
	float m_fEntropyJoint;

	float m_fMutualInfoPrev;
	float m_fNMutualInfoPrev;
	float m_fMutualInfo;
	float m_fNMutualInfo;

	CvFont font;
	CvFont m_Font;

	CvPoint2D32f Features[MAXFeat];
	
	CvPoint2D32f Featurestrackedfw[MAXFeat];
	CvPoint2D32f Featurestrackedbw[MAXFeat];
	
	CvPoint2D32f *m_pFeatureGrid;
	CvPoint2D32f *m_pFeatureGridtracked;
	CvPoint2D32f *m_pFeatureGridtracked2;
	CvPoint2D32f *m_pOpticalflow;
	CvPoint2D32f *m_pOpticalflow2;

	int m_igridwidth;
	int m_igridheight;

	char OptfndFeaturesfw[MAXFeat];
	char OptfndFeaturesbw[MAXFeat];

	char *m_pfndFeatures;
	char *m_pfndFeatures2;

	int m_iFeatureno;

	float OptFeaError[MAXFeat];
	float *m_pOptFeatError;
	CvSize OptWnd;
	int FeatureNo;

	CvTermCriteria OptTerCri; 

	CvMemStorage *Feat1storage;
	CvMemStorage *Feat2storage;

	CvSeq *FeatVec1;
	CvSeq *FeatVec2;

	//background modeling
	CvGaussBGModel* bg_model;

	CvVideoWriter* m_pWriter;

	float FindMedianValue(float *parray, int iLength);
	void optmeanfilter(CvPoint2D32f *pfeature, int winsize);
	void optmedianfilter(CvPoint2D32f *pfeature, int winsize);


public:
	fstream fenergyFile;
	fstream fenergydiffFile;
	fstream fmutualinfoFile;
	fstream fnmutualinfoFile;
	fstream fdynamicFile;
	fstream fforratio;

	OpticalFlow(void) ;

	inline IplImage* getOutputImage()
	{
		return output;
	}

	inline float getEnergy()
	{
		return m_fEnergyfw;
	}
	
	inline float getEnergyDiff()
	{
		return m_fEnergyDiff;
	}

	inline float getMutualInfo()
	{
		return m_fMutualInfo;
	}

	inline float getNMutualInfo()
	{
		return m_fNMutualInfo;
	}
	//to check whether an initialization is required
	bool  isInitialized(IplImage *image);

	void initialize(IplImage *image);


	//the processing of the image
	void process(IplImage *image, int framenum);
	void process2(IplImage *image, int framenum);

	// the method that checks for initialization
	// and then process the image
	inline void processImage(IplImage *image, int framenum, float* penergy, float* penergydiff, float* pmutualinfo, float* pnmutualinfo) 
	{
		if (!isInitialized(image))
		{
			initialize(image);
		}
		process2(image, framenum);
		*penergy = m_fEnergyfw;
		*penergydiff = m_fEnergyDiff;
		*pmutualinfo = m_fMutualInfo;
		*pnmutualinfo = m_fNMutualInfo;
	}

	//draw vectors
	void drawvectors(CvPoint2D32f *Feats, CvPoint2D32f *Featstrked,char *OptfndFeats, int FeatNo, CvScalar Linecolor, float *Energy);
	void drawvector(CvPoint2D32f Feat, double angle, double hypotenuse, char OptfndFeat, CvScalar Linecolor);

	//Mutual Information
	bool mutualinfo(float *fwhist, float *bwhist, float **jointhist);

	void drawtext(IplImage *output, char *text, CvScalar sc, CvPoint point, CvFont *font);

	void resethist(float *fwhist, float *bwhist, float **jointhist);

	// memory de-allocation
	void release();

	void abdetection1(IplImage *image, float fenergy, float fthreshold);
	void abdetection2(IplImage *image, int miprev, int micur);


public:
	~OpticalFlow(void)
	{
		release();
	}
};
